#include "Analysis.h"

typedef struct {
    int length;
    char data[1024];
}Train;


void trainTransfileGets(int fd,int sockfd){
    printf("into trainTransfileGets\n");
    Train train;
    while(1){
        int rret = recv(sockfd,&train.length,sizeof(train.length),MSG_WAITALL);
        if(rret == 0 || train.length == 0){
            printf("trainTransfileGets out\n");
            break;
        }
        recv(sockfd,train.data,train.length,MSG_WAITALL);
        write(fd,train.data,train.length);
    }
    printf("out trainTransfileGets\n");
}

void mmapTransfileGets(int fd,int sockfd,long filesize){
    printf("into mmapTransfileGets\n");
    ftruncate(fd,filesize);
    char * p = (char *)mmap(NULL,filesize,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
    recv(sockfd,p,filesize,MSG_WAITALL);
    msync(p,filesize,MS_SYNC);
    munmap(p,filesize);
    printf("out mmapTransfileGets\n");
    return;
}

int getsDispose(char * str,int sockfd){   //str表示你要服务器哪个位置的文件
    
    char command[] = "gets";
    Train train;
    train.length = strlen(command);            //向后台发送信号
    memcpy(train.data,command,train.length);
    send(sockfd,&train,sizeof(train.length)+train.length,MSG_NOSIGNAL);

    char * p = str;             //发路径
    
    train.length = strlen(p);
    memcpy(train.data,p,train.length);
    send(sockfd,&train,sizeof(train.length)+train.length,MSG_NOSIGNAL);
    
    int length = strlen(p);                   //割出文件名(留给后面客户端没有这个文件时，创建文件)
    int i = length - 1;
    while(i>=0){                    
        if(p[i] == '/'){
            break;
        } 
        --i;
    }
    char filename[64] = {0};
    if(i == -1){
        strcpy(filename,p); 
    }else{
        for(int j = i+1 , k = 0; j < strlen(p) ; ++j , ++k){
            filename[k] = p[j];
        }
    }

    
    int epollfd = epoll_create(1);
    struct epoll_event event;
    event.events = EPOLLIN;
    event.data.fd = sockfd;
    epoll_ctl(epollfd,EPOLL_CTL_ADD,sockfd,&event);
    struct epoll_event readyset[5];
    epoll_wait(sockfd,readyset,5,-1);         //等待服务端发送有没有信号
    char has;                                 //接收是否有
    recv(sockfd,&has,1,MSG_WAITALL);
    printf("接收has:%c\n",has);
    if(has == '0'){
        printf("no find file\n");
        return 0;
    }
    if(has == '1'){
        char isBig;                                 //接收是不是大文件                             
        recv(sockfd,&isBig,1,MSG_WAITALL);
        printf("isBig:%c\n",isBig);

        long filesize = 0;                          //接收文件大小
        recv(sockfd,&train.length,sizeof(train.length),MSG_WAITALL);
        recv(sockfd,train.data,train.length,MSG_WAITALL);
        memcpy(&filesize,train.data,train.length);
        printf("filesize:%ld\n",filesize);

        int fd = open(filename,O_RDWR|O_CREAT|O_TRUNC,0666);    //在当前文件夹下打开并清空文件
        printf("fd:%d\n",fd);

        if(isBig == '1'){
            mmapTransfileGets(fd,sockfd,filesize);
        }
        if(isBig == '0'){
            trainTransfileGets(fd,sockfd);
        }
    }

    return 0;
} 


void trainTransfilePuts(int fd,int sockfd,long sfilesize){
    lseek(fd,sfilesize,SEEK_SET);
    Train train;
    char buff[1024] = {0};
    while(1){
        bzero(buff,sizeof(buff));
        int rret = read(fd,buff,sizeof(buff));
        if(rret == 0){
            train.length = 0;
            send(sockfd,&train,sizeof(train.length)+train.length,MSG_NOSIGNAL);
            break;
        }
        train.length = rret;
        memcpy(train.data,buff,rret);
        send(sockfd,&train,sizeof(train.length)+train.length,MSG_NOSIGNAL);
    }
    return ;
}



void mmapTransfilePuts(int fd,int sockfd,long sfilesize,long cfilesize){
    ftruncate(fd,cfilesize);
    char * p = (char *)mmap(NULL,cfilesize,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
    send(sockfd,p+sfilesize,cfilesize-sfilesize,MSG_NOSIGNAL);
    munmap(p,cfilesize-sfilesize);
    return ;
}


int putsDispose(char * str,int sockfd,char * s_curpath){    // -1 代表失败，0 代表 成功，
    if(str[strlen(str)-1] == '\n'){
        str[strlen(str)-1] = '\0';
    }

    s_curpath[strlen(s_curpath)-1] = '\0';

    char * command = strtok(str," ");

    char * path = strtok(NULL," ");
    printf("localpath:%s\n",path);
    int ret = access(path,F_OK);    //判断一下有没有这个文件
    if(ret != 0){
        printf("文件或目录不存在\n");
        return -1;                   //没有返回-1
    }

    Train train;
    train.length = strlen(command);            //向后台发送puts信号
    memcpy(train.data,command,train.length);
    send(sockfd,&train,sizeof(train.length)+train.length,MSG_NOSIGNAL);
    
    train.length = strlen(s_curpath);         //发送服务器当前路径
    memcpy(train.data,s_curpath,train.length);
    send(sockfd,&train,sizeof(train.length)+train.length,MSG_NOSIGNAL);

    int length = strlen(path);    
    int i = length - 1;
    while(i>=0){                    //从路径中割出文件名
        if(path[i] == '/'){
            break;
        } 
        --i;
    }
    char docu_name[128] = {0};
    if(i == -1){
        strcpy(docu_name,path); 
    }else{
        for(int j = i+1 , k = 0; j < strlen(path) ; ++j , ++k){
            docu_name[k] = path[j];
        }
    }
    printf("name:%s\n",docu_name);
    train.length = strlen(docu_name);            //发送文件名字
    memcpy(train.data,docu_name,train.length);
    send(sockfd,&train,sizeof(train.length)+train.length,MSG_NOSIGNAL);
    

    int fd = open(path,O_RDWR,0666);                 // 打开文件
    struct stat statbuf;
    fstat(fd,&statbuf);
    printf("size:%ld\n",statbuf.st_size);
    train.length = sizeof(statbuf.st_size);             //发送文件大小
    memcpy(train.data ,&statbuf.st_size,train.length);
    send(sockfd,&train,sizeof(train.length)+ train.length ,MSG_NOSIGNAL);
    
    char isBig;
    if(statbuf.st_size < 104857600){
        isBig = '0';
    }else{
        isBig = '1';
    }
    send(sockfd,&isBig,1,MSG_NOSIGNAL);             //发送是不是大文件

    int epollfd = epoll_create(1);
    struct epoll_event event;
    event.events = EPOLLIN;
    event.data.fd = sockfd;
    epoll_ctl(epollfd,EPOLL_CTL_ADD,sockfd,&event);
    struct epoll_event readyset[5];
    epoll_wait(epollfd,readyset,5,-1);

    char has;                                   //接收服务器是否“有”信号
    recv(readyset[0].data.fd,&has,1,MSG_WAITALL);
    printf("接收has:%c\n",has);
    if(has == '0'){
        if(isBig == '1'){
            mmapTransfilePuts(fd,sockfd,0,statbuf.st_size);
        }
        if(isBig == '0'){
            trainTransfilePuts(fd,sockfd,0);
        }
    }
    if(has == '1'){
        long sfilesize = 0;                //接收服务器文件长度
        recv(readyset[0].data.fd,&train.length,sizeof(train.length),MSG_WAITALL);
        recv(readyset[0].data.fd,train.data,train.length,MSG_WAITALL);
        memcpy(&sfilesize,train.data,train.length);

        char md5_str_s[64];      //接收服务器文件md5
        recv(readyset[0].data.fd,&train.length,sizeof(train.length),MSG_WAITALL);
        recv(readyset[0].data.fd,train.data,train.length,MSG_WAITALL);
        memcpy(md5_str_s,train.data,train.length);
        printf("收到server filesize:%ld\n",sfilesize);
        printf("收到server md5:%s\n",md5_str_s);

        if(statbuf.st_size > sfilesize){        //客户机文件长度大于服务器文件长度

            MD5_CTX ctx;
            MD5_Init(&ctx);
            char buff[4096];
            ssize_t readedsize = 0;
            ssize_t remainsize = 0;
            while(1){
                remainsize = sfilesize - readedsize;
                if(remainsize > sizeof(buff) ){
                    bzero(buff,sizeof(buff));
                    ssize_t rret = read(fd,buff,sizeof(buff));
                    readedsize =  readedsize + rret;
                    MD5_Update(&ctx,buff,rret);
                }else{
                    bzero(buff,sizeof(buff));
                    ssize_t rret = read(fd,buff,remainsize);
                    MD5_Update(&ctx,buff,rret);
                    break;
                }
            }
            unsigned char md[16];
            MD5_Final(md,&ctx);
            char md5_str_c[33] = {0};
            char num[3] = {0};
            for(int k =0 ; k < 16 ; ++k){
                sprintf(num,"%02x",md[k]);
                strcat(md5_str_c,num);
            }
            printf("客户机文件一部分的md5:%s\n",md5_str_c);

            if(strcmp(md5_str_c,md5_str_s)==0){
                send(sockfd,"11",2,MSG_NOSIGNAL);    //断点续传
                if(isBig == '1'){
                    mmapTransfilePuts(fd,sockfd,sfilesize,statbuf.st_size);
                }
                if(isBig == '0'){
                    trainTransfilePuts(fd,sockfd,sfilesize);
                }
            }else{
                send(sockfd,"01",2,MSG_NOSIGNAL);   //重传
                if(isBig == '1'){
                    mmapTransfilePuts(fd,sockfd,0,statbuf.st_size);
                }
                if(isBig == '0'){
                    trainTransfilePuts(fd,sockfd,0);
                }
            }
        }else if(statbuf.st_size < sfilesize){
            send(sockfd,"01",2,MSG_NOSIGNAL);
            if(isBig == '1'){
                mmapTransfilePuts(fd,sockfd,0,statbuf.st_size);
            }
            if(isBig == '0'){
                trainTransfilePuts(fd,sockfd,0);
            }
        }else{
            MD5_CTX ctx;
            MD5_Init(&ctx);
            char buff[4096];
            while(1){
                bzero(buff,sizeof(buff));
                ssize_t rret = read(fd,buff,sizeof(buff));
                if(rret == 0){
                    break;
                }              
                MD5_Update(&ctx,buff,rret);
            }
            unsigned char md[16];
            MD5_Final(md,&ctx);
            char md5_str_c[33] = {0};
            char num[3] = {0};
            for(int k =0 ; k < 16 ; ++k){
                sprintf(num,"%02x",md[k]);
                strcat(md5_str_c,num);
            }

            if(strcmp(md5_str_c,md5_str_s)!=0){    
                send(sockfd,"01",2,MSG_NOSIGNAL);
                if(isBig == '1'){
                    mmapTransfilePuts(fd,sockfd,0,statbuf.st_size);
                }
                if(isBig == '0'){
                    trainTransfilePuts(fd,sockfd,0);
                }
            }else{
                send(sockfd,"10",2,MSG_NOSIGNAL);
            }
        }
    }


    return 0;
}


void mkdirDispose(char * str,int sockfd){
    char command[] = "mkdir";
    Train train;
    train.length = strlen(command);            //向后台发送信号
    memcpy(train.data,command,train.length);
    send(sockfd,&train,sizeof(train.length)+train.length,MSG_NOSIGNAL);
    
    train.length = strlen(str);
    memcpy(train.data,str,train.length);
    send(sockfd,&train,sizeof(train.length)+train.length,MSG_NOSIGNAL);

    int epollfd = epoll_create(1);
    struct epoll_event event;
    event.events = EPOLLIN;
    event.data.fd = sockfd;
    epoll_ctl(epollfd,EPOLL_CTL_ADD,sockfd,&event);
    struct epoll_event readyset[5];
    epoll_wait(epollfd,readyset,5,-1);

    char c;
    recv(readyset[0].data.fd,&c,1,MSG_WAITALL);
    if(c=='1'){
        printf("创建文件成功！\n");
    }
    else{
        printf("创建失败！\n");
    }
    return ;
}

void rmdirDispose(char * str,int sockfd){
    char command[] = "rmdir";
    Train train;
    train.length = strlen(command);            //向后台发送信号
    memcpy(train.data,command,train.length);
    send(sockfd,&train,sizeof(train.length)+train.length,MSG_NOSIGNAL);
    
    train.length = strlen(str);
    memcpy(train.data,str,train.length);
    send(sockfd,&train,sizeof(train.length)+train.length,MSG_NOSIGNAL);

    int epollfd = epoll_create(1);
    struct epoll_event event;
    event.events = EPOLLIN;
    event.data.fd = sockfd;
    epoll_ctl(epollfd,EPOLL_CTL_ADD,sockfd,&event);
    struct epoll_event readyset[5];
    epoll_wait(epollfd,readyset,5,-1);

    char c;
    recv(readyset[0].data.fd,&c,1,MSG_WAITALL);
    if(c=='1'){
        printf("空文件删除成功！\n");
    }
    else{
        printf("空文件删除失败！\n");
    }
    return ;
}


void rmfileDispose(char * str,int sockfd){
    char command[] = "remove";
    Train train;
    train.length = strlen(command);            //向后台发送信号
    memcpy(train.data,command,train.length);
    send(sockfd,&train,sizeof(train.length)+train.length,MSG_NOSIGNAL);
    
    train.length = strlen(str);
    memcpy(train.data,str,train.length);
    send(sockfd,&train,sizeof(train.length)+train.length,MSG_NOSIGNAL);

    int epollfd = epoll_create(1);
    struct epoll_event event;
    event.events = EPOLLIN;
    event.data.fd = sockfd;
    epoll_ctl(epollfd,EPOLL_CTL_ADD,sockfd,&event);
    struct epoll_event readyset[5];
    epoll_wait(epollfd,readyset,5,-1);

    char c;
    recv(readyset[0].data.fd,&c,1,MSG_WAITALL);
    if(c=='1'){
        printf("文件删除成功！\n");
    }
    else{
        printf("文件删除失败！\n");
    }
    return ;

}


void lsDispose(Stack * s,int sockfd){
    Train train;

    char command[] = "ls";
    train.length = strlen(command);            //向后台发送信号
    memcpy(train.data,command,train.length);
    send(sockfd,&train,sizeof(train.length)+train.length,MSG_NOSIGNAL);

    if(s->size == 1){
        train.length = 1;
        memcpy(train.data,"/",train.length);
    }else{
        char path[1024] = {0};
        for(int i = 1 ; i < s->size ; ++i){
            char xiegang[] = "/";
            char dirname[128] = {0};
            memcpy(dirname,s->str[i],strlen(s->str[i]));
            strcat(path,xiegang);
            strcat(path,dirname);
        }
        train.length = strlen(path);
        memcpy(train.data,path,train.length);
        printf("当前目录为:%s\n",path);
    }
    send(sockfd,&train,sizeof(train.length)+train.length,MSG_NOSIGNAL);
    
    int epollfd = epoll_create(1);
    struct epoll_event event;
    event.events = EPOLLIN;
    event.data.fd = sockfd;
    epoll_ctl(epollfd,EPOLL_CTL_ADD,sockfd,&event);
    struct epoll_event readyset[5];
    epoll_wait(epollfd,readyset,5,-1);

    int num = -1;
    while(1){
        recv(sockfd,&train.length,sizeof(train.length),MSG_WAITALL);
        if(train.length == 0){
            break;
        }
        recv(sockfd,train.data,train.length,MSG_WAITALL);
        char name[256] = {0};
        memcpy(name,train.data,train.length);
        unsigned char type;
        recv(sockfd,&type,sizeof(unsigned char),MSG_WAITALL);
        if(strcmp(name,".") == 0 || strcmp(name,"..")==0){
            continue;
        }
        if(type == DT_DIR){
            printf(BLUE "%s\t" RESET,name);
        }else{
            printf("%s\t",name);
        }
        if(num%5 == 4){
            printf("\n");
        }
        ++num;
    }
    printf("\n");
}

void pwdDispose(Stack * s){
    if(s->size == 1){
        printf("/\n");
        fflush(stdout);
    }else{
    for(int i = 1; i < s->size;++i){
        printf("/%s",s->str[i]);
        fflush(stdout);
        }
    }
    printf("\n");
    return ;
}


char * checkPath(char * str,Stack * s){       
   if(str[strlen(str)-1]=='\n'){
        str[strlen(str)-1] = '\0';
    }

      

    Stack ass_s;
    stackInit(&ass_s);
     for(int i = 0  ; i <  s->size ; ++i){    //深拷贝
        char * ass_str = (char *)calloc(strlen(s->str[i])+1,sizeof(char));
        strncpy(ass_str,s->str[i],strlen(s->str[i]));
        push(&ass_s,ass_str);
    }


    int nn = 0;
    while(str[nn]!=' '){
        ++nn;
    }
    while(str[nn]==' '){
        ++nn;
    }
    if( str[nn] == '/'){                    
        destroyStack(&ass_s);
        char * aaaa = (char *)calloc(2,sizeof(char));
        aaaa[0] = '/';
        push(&ass_s,aaaa);
        if(nn+1==strlen(str)){
            printf("111\n");
            printf("客户端产生服务器要 gets的path:%s\n",aaaa);   
            return aaaa;
        }
        for(int i = nn ; i < strlen(str) ; ++i){
            str[i] = str[i+1];
        }
    }

    strtok(str," ");    //剔除puts
        
    

    while(!isEmpty(&ass_s)){                   //检查路径是否合理，并得出屏幕应该输出的合理路径。
        char * p = strtok(NULL,"/");
        if(p == NULL){
            break;
        }
        if(strcmp(p,"~")==0){
            destroyStack(&ass_s);
            char * bbbb = (char *)calloc(5,sizeof(char));
            bbbb[0] = '~';
            push(&ass_s,bbbb);
            continue;
        }
        if(strcmp(p,".")==0){
            continue;
        }
        if(strcmp(p,"..")== 0){
            free(top(&ass_s));
            pop(&ass_s);
            continue;
        }
        char * ass_str = (char*)calloc(strlen(p)+1,sizeof(char));
        strncpy(ass_str,p,strlen(p));
        push(&ass_s,ass_str);
    }

    if(isEmpty(&ass_s)){        //恢复路径栈内容
        return NULL;
    }

    if(ass_s.size == 1){
        char * cccc = (char *)calloc(2,sizeof(char));
        cccc[0] = '/';
        printf("222\n");
        printf("客户端产生服务器要 gets的path:%s\n",cccc);   

        return cccc;
    }
    //栈不为空，说明路径合理。
    char * path = (char *)calloc(1024,sizeof(char));      //遍历s，得出当前服务器路径(一部分)  /dsf/dsf/fsd
    for(int i = 1 ; i < ass_s.size ; ++i){
        char xiegang[] = "/";
        char sstr[128]={0};
        memcpy(sstr,ass_s.str[i],strlen(ass_s.str[i]));
        strcat(path,xiegang);
        strcat(path,sstr);
    } 
    printf("3333\n");
    printf("客户端产生服务器要 gets的path:%s\n",path);   

    return path;
    
}


int cdDispose(char * str ,int sockfd,Stack * s){
    if(str[strlen(str)-1]=='\n'){
        str[strlen(str)-1] = '\0';
    }

    Stack ass_s;
    stackInit(&ass_s);
    for(int i = s->size-1  ; i >= 0 ; --i){    //深拷贝    记录原始路径
        char * ass_str = (char *)calloc(strlen(s->str[i])+1,sizeof(char));
        strncpy(ass_str,s->str[i],strlen(s->str[i]));
        push(&ass_s,ass_str);
    }
    
    int nn = 0;
    while(str[nn]!=' '){
        ++nn;
    }
    while(str[nn]==' '){
        ++nn;
    }
    if(str[nn] == '/'){                    
        destroyStack(s);
        char * aaaa = (char *)calloc(2,sizeof(char));
        aaaa[0] = '/';
        push(s,aaaa);
        if(nn+1==strlen(str)){
            return 0;
        }
        for(int i = nn ; i < strlen(str) ; ++i){
            str[i] = str[i+1];
        }
    }
    printf("str:%s\n",str);

    char * command = strtok(str," ");    //记录cd命令
        
    

    while(!isEmpty(s)){                   //检查路径是否合理，并得出屏幕应该输出的合理路径。
        char * p = strtok(NULL,"/");
        if(p == NULL){
            break;
        }
        if(strcmp(p,"~")==0){
            destroyStack(s);
            char * bbbb = (char *)calloc(5,sizeof(char));
            bbbb[0] = '~';
            push(s,bbbb);
            continue;
        }
        if(strcmp(p,".")==0){
            continue;
        }
        if(strcmp(p,"..")== 0){
            free(top(s));
            pop(s);
            continue;
        }
        char * ass_str = (char*)calloc(strlen(p)+1,sizeof(char));
        strncpy(ass_str,p,strlen(p));
        push(s,ass_str);
    }

    if(isEmpty(s)){        //恢复路径栈内容
        printf("路径输入错误！\n");
        while(!isEmpty(&ass_s)){
            push(s,top(&ass_s));
            pop(&ass_s);
        }
        return -1;
    }

    if(s->size == 1){
        return 0;
    }
    //栈不为空，说明路径合理。
    char path[1024] = {0};      //遍历s，得出当前服务器路径(一部分)  /dsf/dsf/fsd
    for(int i = 1 ; i < s->size ; ++i){
        char xiegang[] = "/";
        char sstr[128]={0};
        memcpy(sstr,s->str[i],strlen(s->str[i]));
        strcat(path,xiegang);
        strcat(path,sstr);
    }                  
    printf("path:%s\n",path);   

    Train train; //向后台发送cd信号
    train.length = strlen(command);
    memcpy(train.data,command,train.length);
    send(sockfd,&train,sizeof(train.length)+train.length,MSG_NOSIGNAL);
    
    train.length = strlen(path);   //发送目录文件路径            
    memcpy(train.data,path,train.length);
    send(sockfd,&train,sizeof(train.length)+train.length,MSG_NOSIGNAL);
    
    int epollfd = epoll_create(1);       //等待接收“是否有”信号
    struct epoll_event event;
    event.events = EPOLLIN;
    event.data.fd = sockfd;
    epoll_ctl(epollfd,EPOLL_CTL_ADD,sockfd,&event);
    struct epoll_event readyset[5];
    epoll_wait(epollfd,readyset,5,-1);
    
    char has;
    recv(readyset[0].data.fd,&has,1,MSG_WAITALL);
    printf("%c\n",has);

    if(has == '0'){
        printf("目录或文件不存在\n");
        destroyStack(s);          //清空路径栈
        while(!isEmpty(&ass_s)){  //恢复路径栈
            push(s,top(&ass_s));
            pop(&ass_s);
        }
        destroyStack(&ass_s); 
        return -1;
    }

    return 0;
}







int analysis(char * str,int sockfd){
    str[strlen(str)-1] = '\0';
    char ass_str[20] = {0};                     //拆出指令
    strncpy(ass_str,str,sizeof(ass_str)-1);
    ass_str[sizeof(ass_str)-1] = '\0';
    char *p = strtok(ass_str," ");
    if(strcmp(p,"cd")==0){                      //解析指令
        return 0;
    }
    if(strcmp(p,"ls")==0){
        return 1;
    }
    if(strcmp(p,"pwd")==0){
        return 2;
    }
    if(strcmp(p,"mkdir")==0){
        return 3;
    }
    if(strcmp(p,"rmdir")==0){
        return 4;
    }
    if(strcmp(p,"remove") == 0){
        return 5;
    }
    if(strcmp(p,"puts")==0){
        return 6;
    }
    if(strcmp(p,"gets")==0){
        return 7;
    }
    return 8;
   // while(!isEmpty(s)){
   //     p = strtok(NULL,"/");
   //     if(p == NULL){
   //         break;
   //     }
   //     if(strcmp(p,".") == 0){
   //         continue;
   //     }
   //     if(strcmp(p,"..") == 0){
   //         pop(s);
   //         continue;
   //     }
   //     push(s,p); 
   // }

}
